#include <pthread.h>
#include <mqueue.h>
#include <stdio.h>
#include "face.h"
#include "myoled.h"
#include <wiringPi.h>
#include "control.h"
#include "receive_interface.h"
#include "msg_queue.h"
#include "global.h"
#include "gdevice.h"
#include "lrled_gdevice.h"
#include "brled_gdevice.h"
#include "fan_gdevice.h"
#include "beep_gdevice.h"
#include "lock_gdevice.h"

typedef struct {
    int msg_len;
    unsigned char *buffer;
    ctrl_info_t *ctrl_info;
} recv_msg_t;


static int oled_fd = -1;
static struct gdevice *pdevhead = NULL;
static int receive_init(void)
{
    //设备链表添加
    pdevhead = add_lrled_to_gdevice_list(pdevhead);
    pdevhead = add_brled_to_gdevice_list(pdevhead);
    pdevhead = add_fan_to_gdevice_list(pdevhead);
    pdevhead = add_beep_to_gdevice_list(pdevhead);
    pdevhead = add_lock_to_gdevice_list(pdevhead);
    //因为消息队列接收线程需要控制人脸识别 oled显示
    oled_fd = myoled_init();
    face_init();
    return 0;
    
}
static void receive_final(void)
{
    face_final();
    if(oled_fd != -1)
    {
        close(oled_fd);
        oled_fd = -1;
    }
}

static void *handle_device(void *arg)
{
    recv_msg_t *recv_msg = NULL;
    struct gdevice *cur_gdev = NULL;
    int ret = -1;//用于判断GPIO电瓶状态设置是否成功
    char success_or_fail[20] = "success";//指令执行成功或失败信息
    int smoke_status = 0;
    pthread_t tid = -1;
    double face_result = 0.0;
    pthread_detach(pthread_self());

    if (arg != NULL)//根据传入的参数传入的参数arg 赋值给 recv_msg结构体
    {
        recv_msg = (recv_msg_t*)arg;
        printf("recv_msg->msg_len=%d\n",recv_msg->msg_len);
        printf("%s|%s|%d:handle 0x%x, 0x%x,0x%x, 0x%x, 0x%x,0x%x\n",__FILE__, __func__, __LINE__, recv_msg->buffer[0], recv_msg->buffer[1], recv_msg->buffer[2], recv_msg->buffer[3], recv_msg->buffer[4],recv_msg->buffer[5]);
    }
    
    //do something
    if (recv_msg->buffer != NULL && recv_msg != NULL)
        cur_gdev = find_gdevice_by_key(pdevhead, recv_msg->buffer[2]);//根据recv_msg->buffer[2]中的数据找到对应的设备cur_gdev设备结构体指针
    // printf("%s|%s|%d\n",__FILE__, __func__, __LINE__);

    if (cur_gdev != NULL)
    {
        cur_gdev->gpio_status = recv_msg->buffer[3] == 0 ? LOW : HIGH; //根据recv_msg->buffer[3]中的数据设置该设备的gpio_status

        // special for lock
        if (cur_gdev->check_face_status == 1)//如果需要人脸识别
        {
            face_result = face_category();//人脸检测
            if  (face_result > 0.6)
            {
                ret = set_gpio_gdevice_status(cur_gdev);
                recv_msg->buffer[2] = 0x47; //设置buffer之后调用语音播报
            }
            else
            {
                recv_msg->buffer[2] = 0x46;
                ret = -1;
            }
        }
        else if (cur_gdev->check_face_status == 0)//如果不需要人脸指点设置电平状态
        {
            ret = set_gpio_gdevice_status(cur_gdev);
            printf("%s|%s|%d: set status\n",__FILE__, __func__, __LINE__);
        }
    }
    
    
    if (cur_gdev != NULL && cur_gdev->voice_set_status == 1)//判断是否需要语音播报
    {
        if (recv_msg != NULL && recv_msg->ctrl_info != NULL && recv_msg->ctrl_info->ctrl_phead != NULL)
        {
            struct control *pcontrol = recv_msg->ctrl_info->ctrl_phead;
            while (pcontrol != NULL)//遍历找到voice设备并使用set()播报函数
            {
                if (strstr(pcontrol->control_name, "voice"))
                {
                    if (recv_msg->buffer[2] == 0x45 && recv_msg->buffer[3] == 0)//如果是火灾警报45 00
                    {
                        smoke_status = 1;//修改状态值为了改变oled显示的内容 
                    }   
                    pthread_create(&tid, NULL, pcontrol->set, (void*)recv_msg->buffer);//其余情况都根据buffer进行语音播报
                    break;   
                }
                pcontrol = pcontrol->next;
            }
        }
        
    }
    

    if (ret == -1) //如果设置电瓶状态失败则打印failed
    {
        memset(success_or_fail,'\0',sizeof(success_or_fail));//确保oled打印的内容正确不会保留上一条内容的信息
        strncpy(success_or_fail,"failed",6);
    }

    //oled显示
    char olde_msg[512];
    memset(olde_msg,0,sizeof(olde_msg));
    printf("%s|%s|%d\n",__FILE__, __func__, __LINE__);
    if (cur_gdev != NULL)
    {
        memset(olde_msg,0,sizeof(olde_msg));
        char *change_status = cur_gdev->gpio_status == LOW ? "Open" : "Close";
        sprintf(olde_msg,"%s %s %s!",change_status, cur_gdev->dev_name, success_or_fail);
        if (smoke_status == 1) //special for smoke
        {
            memset(olde_msg,0,sizeof(olde_msg));
            strcpy(olde_msg,"A risk of fire");
        }
        printf("olde_msg = %s\n",olde_msg);
        oled_show(olde_msg);
    }
    printf("%s|%s|%d\n",__FILE__, __func__, __LINE__);
    //开锁后五秒关闭锁
    if (cur_gdev != NULL && cur_gdev->check_face_status==1 && ret == 0 && face_result>0.6)
    {
        sleep(5);
        cur_gdev->gpio_status = HIGH;
        set_gpio_gdevice_status(cur_gdev);
    }
    printf("%s|%s|%d\n",__FILE__, __func__, __LINE__);
    pthread_exit(0);
}

static void* receive_get(void *arg)
{
    recv_msg_t *recv_msg = NULL;
    ssize_t read_len = -1;//读取队列得到的字节数
    struct mq_attr attr;//队列参数结构体
    char *buffer = NULL;//用作与接收到的数据进行判断

    pthread_t tid = -1;

    if (arg != NULL)
    {
        recv_msg = (recv_msg_t*)malloc(sizeof(recv_msg_t));
        recv_msg->ctrl_info = (ctrl_info_t*)arg;//获取到mqd和control链表的头结点
        recv_msg->msg_len = -1;
        recv_msg->buffer = NULL;
    }
    else{
        pthread_exit(0);
    }
    
    //获取队列的属性如消息数,最大消息长度
    if(mq_getattr(recv_msg->ctrl_info->mqd, &attr) == -1)
    {
        pthread_exit(0);
    }

    //根据队列消息最大长度申请buffer的内存大小
    recv_msg->buffer = (unsigned char *)malloc(attr.mq_msgsize);
    memset(recv_msg->buffer,0,attr.mq_msgsize);
    buffer = (unsigned char *)malloc(attr.mq_msgsize);
    memset(buffer,0,attr.mq_msgsize);
    pthread_detach(pthread_self());//因为子线程是死循环需要和主线程做分离

    while(1)
    {
        //读取队列将数据保存至buffer
        read_len = mq_receive(recv_msg->ctrl_info->mqd, buffer, attr.mq_msgsize, NULL);
        printf("%s|%s|%d:read 0x%x, 0x%x,0x%x, 0x%x, 0x%x,0x%x\n",__FILE__, __func__, __LINE__, buffer[0], buffer[1], buffer[2], buffer[3], buffer[4],buffer[5]);
        printf("%s|%s|%d:read_len=%ld\n", __FILE__, __func__, __LINE__, read_len);    
        if (read_len == -1)
        {
            if(errno == EAGAIN)//表示队列是空的
                pthread_exit(0);
            else
            {
                break;
            }
        }
        else if (buffer[0] == 0xAA && buffer[1] == 0x55 && buffer[5] == 0xAA && buffer[4] == 0x55)
        {
            recv_msg->msg_len = read_len;
            memcpy(recv_msg->buffer, buffer, read_len);//将数据拷贝到recv_msg->buffer
            //创建子线程前进行备份否则buffer会被覆盖
            pthread_create(&tid, NULL, handle_device, (void*)recv_msg);
        }
    }
    pthread_exit(0);
}

struct control receive_control = {
    .control_name = "receive",
    .init = receive_init,
    .final = receive_final,
    .get = receive_get,
    .set = NULL,
    .next = NULL
};

struct control *add_receive_to_ctrl_list(struct control *phead)
{
    //头插法
    return add_device_to_ctrl_list(phead, &receive_control);
}